/*
 * imageio.c
 *
 *  Created on: Mar 28, 2009
 *      Author: gustavo
 */
#include <stdio.h>

#include "imageio.h"

#include "../image.h"
#include "../../matrix/matrix.h"

void image_save_matrix_as_image(Matrix * M, char * fname) {
    Matrix * Tmp = NULL;
    Image * Img = NULL;

    matrix_get_normailzed(M, &Tmp, 0, 255);
    image_mat2gray(Tmp, &Img);
    image_save_pgm(Img, fname);

    matrix_delete(Tmp);
    image_delete(Img);
}

int image_save_pgm(Image * im, char * fname) {
    FILE* fp;

    int width = im->width;
    int height = im->height;
    unsigned char * img = im->data;


    if ((fp = fopen(fname, "w")) == NULL) {
        fprintf(stderr, "Could not open file %s for writing.\n", fname);
        return 0;
    }

    fprintf(fp, "P5\n%d %d\n255\n", width, height);

    int index;
    int size = width * height;
    for (index = 0; index < size; ++index) {
        putc(img[index], fp);
    }

    fflush(fp);
    fclose(fp);
    return 1;
}

int image_save_ppm(Image * im, char * fname) {
    FILE* fp;

    int width = im->width;
    int height = im->height;
    unsigned char * img = im->data;

    if ((fp = fopen(fname, "w")) == NULL) {
        fprintf(stderr, "Could not open file %s for writing.\n", fname);
        return 0;
    }

    fprintf(fp, "P6\n%d %d\n255\n", width, height);

    int index;
    int size = width * height;
    for (index = 0; index < size; ++index) {
        putc(img[index * 3 + 0], fp);
        putc(img[index * 3 + 1], fp);
        putc(img[index * 3 + 2], fp);
    }

    fflush(fp);
    fclose(fp);
    return 1;
}

Image * image_read_ppm(char * filename) {
    int width;
    int height;
    int idx;
    int size;
    int max_value;
    char ch;
    int ret;

    FILE * file;
    Image * img;

    file = fopen(filename, "rb");
    if (file == 0) {
        //print_error(stderr, "File binary open error.");
        //fclose ( f );
        return NULL;
    }

    /* Ler cabecalho */
    if (fscanf(file, "P%c\n", &ch) != 1 || ch != '6') {
        //print_error(stderr, "Invalid file.");
        fclose(file);
        return NULL;
    }

    /* Desconsidera comentarios */
    ch = getc(file);
    while (ch == '#') {
        do {
            ch = getc(file);
        } while (ch != '\n'); /* Le ate o final da linha */
        ch = getc(file);
    }
    ungetc(ch, file);
    ret = fscanf(file, "%d %d\n", &width, &height);
    /* idx esta funcionando como uma variavel inteira temportaria */
    ret = fscanf(file, "%d\n", &idx);
    max_value = idx;

    /* Salva os parametros da imagem */
    img = image_new(width, height, IMAGE_PALETTE_RGB);

    if (img == NULL) {
        //print_error(stderr, "Unable to alloc memory to image.");
        fclose(file);
        return NULL;
    }

    /* Leitura dos pixels */
    size = img->width * img->height * img->bands;
    for (idx = 0; idx < size; idx++) {
        ret = fread(&ch, sizeof (char), 1, file);
        img->data[idx] = (float) ch;
    }
    /* fread ( img->data, sizeof (char), img->width * img->height * img->n_channels, f ); */

    fclose(file);
    return img;
}

Image * image_read_pgm(char * filename) {
    int width;
    int height;
    int idx;
    int size;
    double max_value;
    char ch;
    int ret;

    FILE * f;
    Image * img;

    f = fopen(filename, "rb");
    if (f == 0) {
        //print_error(stderr, "File binary open error.");
        //fclose ( f );
        return NULL;
    }

    /* Ler cabecalho */
    if (fscanf(f, "P%c\n", &ch) != 1 || ch != '5') {
        //print_error(stderr, "Invalid file.");
        fclose(f);
        return NULL;
    }

    /* Desconsidera comentarios */
    ch = getc(f);
    while (ch == '#') {
        do {
            ch = getc(f);
        } while (ch != '\n'); /* Le ate o final da linha */
        ch = getc(f);
    }
    ungetc(ch, f);
    ret = fscanf(f, "%d %d\n", &width, &height);
    /* idx esta funcionando como uma variavel inteira temportaria */
    ret = fscanf(f, "%d\n", &idx);
    max_value = (double) idx;

    /* Salva os parametros da imagem */
    img = image_new(width, height, IMAGE_PALETTE_GRAY);

    if (img == NULL) {
        //print_error(stderr, "Unable to alloc memory to image.");
        fclose(f);
        return NULL;
    }

    /* Leitura dos pixels */
    size = img->width * img->height * img->bands;
    for (idx = 0; idx < size; idx++) {
        ret = fread(&ch, sizeof (char), 1, f);
        img->data[idx] = (float) ch;
    }
    /* fread ( img->data, sizeof (char), img->width * img->height * img->n_channels, f ); */

    fclose(f);
    return img;
}



//=============================================================================
//= Estruturas do formato BMP =================================================

typedef struct _BMP_header {
    char bfType[2]; // "BMv?"
    int bfSize; // Tamanho do arquivo
    short int bfReserved1;
    short int bfReserved2;
    int bfOffsetBits; // Deslocamento (em bytes) para o in�cio da �rea de dados
} BMP_header;

typedef struct _BMP_info_header {
    int biSize; // Tamanho deste cabe�alho. Sempre 40bytes ou 28h
    int biWidth; // Largura da imagem
    int biHeight; // Altura da imagem
    short int biPlanes; // N�mero de planos da imagem, sempre = 1
    short int biBitCount; // N�mero de bits por pixel (1,4,8,24,32)
    int biCompression; // Tipo de compress�o usada.
    //	- 0: BI_RGB; Sem compressao
    //	- 1: BI_RLE8; compressao RLE 8 bits
    //	- 2: BI_RLE4; compressao RLE 4 bits
    int biSizeImage; // Tamanho da imagem em bytes
    int biXPixelsPerMeter; // Resolu��o horizontal. Pixels/Metro
    int biYPixelsPerMeter; // Resolu��o vertical. Pixels/Metro
    int biClrUsed; // Numero de cores usada na imagem. Se for 0, indica o uso
    // do m�ximo de cores poss�vel: 2^BiBitCount
    int biClrImportant; // Numero de cores realmente usada na imagem. Se 0, indica
    // que todas s�o importantes
    char * Paleta; // Paleta de cores: 4bytes x n�mero de cores.
    // Na ordem: Blue, Green, Red, ZERO
} BMP_info_header;

/*===========================================================================================*/

Image * image_read_bmp(char * filename) {
    FILE * f;
    BMP_header bmp_header;
    BMP_info_header infoHeader;

    Image * img;

    int idx;
    char px;
    int row, col, alignBytes;
    int scanLine, offLine;
    int t;

    f = fopen(filename, "rb");
    if (f == 0) {
        fprintf(stderr, "File binary open error.");
        return NULL;
    }

    t = fread(&bmp_header.bfType, sizeof (char), 2, f);
    t = fread(&bmp_header.bfSize, sizeof (int), 1, f);
    t = fread(&bmp_header.bfReserved1, sizeof (short int), 1, f);
    t = fread(&bmp_header.bfReserved2, sizeof (short int), 1, f);
    t = fread(&bmp_header.bfOffsetBits, sizeof (int), 1, f);

    t = fread(&infoHeader.biSize, sizeof (int), 1, f);
    t = fread(&infoHeader.biWidth, sizeof (int), 1, f);
    t = fread(&infoHeader.biHeight, sizeof (int), 1, f);
    t = fread(&infoHeader.biPlanes, sizeof (short int), 1, f);
    t = fread(&infoHeader.biBitCount, sizeof (short int), 1, f);
    t = fread(&infoHeader.biCompression, sizeof (int), 1, f);
    t = fread(&infoHeader.biSizeImage, sizeof (int), 1, f);
    t = fread(&infoHeader.biXPixelsPerMeter, sizeof (int), 1, f);
    t = fread(&infoHeader.biYPixelsPerMeter, sizeof (int), 1, f);
    t = fread(&infoHeader.biClrUsed, sizeof (int), 1, f);
    t = fread(&infoHeader.biClrImportant, sizeof (int), 1, f);

    if (infoHeader.biBitCount != 24) {
        fprintf(stderr, "BMP file is not 24 bits. This version supports only 24 bits.");
        fclose(f);
        return NULL;
    }

    if (infoHeader.biCompression != 0) {
        fprintf(stderr, "BMP file is compressed.");
        fclose(f);
        return NULL;
    }

    /* To BMP 24 bits, the color paleta is null */
    img = image_new(infoHeader.biWidth, infoHeader.biHeight, IMAGE_PALETTE_RGB);
    /*
            img->width = infoHeader.biWidth;
            img->height = infoHeader.biHeight;
            img->bands = 3;
            img->palette = IMAGE_PALETTE_RGB;
            img->size = img->width * img->height * (img->bands);
            img->data = (char *) calloc(1, sizeof (double) * img->size);
     */


    scanLine = infoHeader.biWidth * 3;
    offLine = 0;
    while ((scanLine % 4) != 0) {
        scanLine++;
        offLine++;
    }

    for (row = img->height - 1; row >= 0; row--) {
        for (col = 0; col < img->width; col++) {

            idx = (row * img->width + col) * 3;

            t = fread(&px, sizeof (char), 1, f);
            img->data[ idx + 2 ] = px;

            t = fread(&px, sizeof (char), 1, f);
            img->data[ idx + 1 ] = px;

            t = fread(&px, sizeof (char), 1, f);
            img->data[ idx ] = px;

            /*
                                    t = fread(&px, sizeof (char), 1, f);
                                    pPX(img, row, col, 2) = (double) px;
                                    t = fread(&px, sizeof (char), 1, f);
                                    pPX(img, row, col, 1) = (double) px;
                                    t = fread(&px, sizeof (char), 1, f);
                                    pPX(img, row, col, 0) = (double) px;
             */
        }
        for (col = 0; col < offLine; col++) {
            t = fread(& (alignBytes), sizeof (char), 1, f);
        }
    }

    fclose(f);
    return img;
}

int image_save_bmp(Image * img, char *filename) {
    BMP_header fileHeader;
    BMP_info_header infoHeader;
    FILE *file;
    int scanLine, offLine;
    long int col, row;
    char blackColor = 0;
    char px;
    int idx;

    fileHeader.bfType[0] = 'B';
    fileHeader.bfType[1] = 'M';
    fileHeader.bfReserved1 = 0;
    fileHeader.bfReserved2 = 0;
    fileHeader.bfOffsetBits = 14 + 40;

    infoHeader.biSize = 40;
    infoHeader.biWidth = img->width;
    infoHeader.biHeight = img->height;
    infoHeader.biPlanes = 1;
    infoHeader.biBitCount = 24;
    infoHeader.biCompression = 0;
    infoHeader.biSizeImage = 0;
    infoHeader.biXPixelsPerMeter = 0;
    infoHeader.biYPixelsPerMeter = 0;
    infoHeader.biClrUsed = 0;
    infoHeader.biClrImportant = 0;

    scanLine = img->width * 3;
    offLine = 0;
    while ((scanLine % 4) != 0) {
        scanLine++;
        offLine++;
    }

    fileHeader.bfSize = fileHeader.bfOffsetBits + (scanLine * img->height);

    file = fopen(filename, "wb");
    if (file == NULL) return -1;

    fwrite(& (fileHeader.bfType), sizeof (char), 2, file);
    fwrite(& (fileHeader.bfSize), sizeof (int), 1, file);
    fwrite(& (fileHeader.bfReserved1), sizeof (short int), 1, file);
    fwrite(& (fileHeader.bfReserved2), sizeof (short int), 1, file);
    fwrite(& (fileHeader.bfOffsetBits), sizeof (int), 1, file);

    fwrite(& (infoHeader.biSize), sizeof (int), 1, file);
    fwrite(& (infoHeader.biWidth), sizeof (int), 1, file);
    fwrite(& (infoHeader.biHeight), sizeof (int), 1, file);
    fwrite(& (infoHeader.biPlanes), sizeof (short int), 1, file);
    fwrite(& (infoHeader.biBitCount), sizeof (short int), 1, file);
    fwrite(& (infoHeader.biCompression), sizeof (int), 1, file);
    fwrite(& (infoHeader.biSizeImage), sizeof (int), 1, file);
    fwrite(& (infoHeader.biXPixelsPerMeter), sizeof (int), 1, file);
    fwrite(& (infoHeader.biYPixelsPerMeter), sizeof (int), 1, file);
    fwrite(& (infoHeader.biClrUsed), sizeof (int), 1, file);
    fwrite(& (infoHeader.biClrImportant), sizeof (int), 1, file);


    for (row = img->height - 1; row >= 0; row--) {
        for (col = 0; col < img->width; col++) {

            idx = (row * img->width + col) * 3;

            px = img->data[idx + 2];
            fwrite(&px, sizeof (char), 1, file);
            px = img->data[idx + 1];
            fwrite(&px, sizeof (char), 1, file);
            px = img->data[idx];
            fwrite(&px, sizeof (char), 1, file);
            /*
                                    px = (char) pPX(img, row, col, 2);
                                    fwrite(&px, sizeof (char), 1, file);
                                    px = (char) pPX(img, row, col, 1);
                                    fwrite(&px, sizeof (char), 1, file);
                                    px = (char) pPX(img, row, col, 0);
                                    fwrite(&px, sizeof (char), 1, file);
             */
        }
        for (col = 0; col < offLine; col++) {
            fwrite(& (blackColor), sizeof (char), 1, file);
        }
    }
    fclose(file);
    return 1;
}
